package com.jcj.businesspool;

import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.protobuf.ByteString;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;

/**
 * 测试基于Protobuf的实体对象传输,测试自定义业务线程池,非阻塞处理长耗时业务
 */
@ChannelHandler.Sharable
public class HandlerClientBP extends SimpleChannelInboundHandler<Object> {

    //创建自定义业务线程池,用于非阻塞处理长耗时业务
    protected static ExecutorService newFixedThreadPool() {
        final ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("netty-business-%d")
                .setDaemon(true) //默认为false用户线程,这里设置为true守护线程.注意区别:主线程结束后用户线程还会继续运行,守护线程则会自动结束
                .build();

        return new ThreadPoolExecutor(
                200,//核心线程数
                200,//线程池中能拥有的最多线程数
                0L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(1000),//10000表示用于缓存任务的阻塞队列,其实可以理解为最大并发量
                threadFactory);
    }

    final static ListeningExecutorService service = MoreExecutors.listeningDecorator(newFixedThreadPool());


    /**
     * 处理接收到的消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

        //读取消息,得到反序列后的实体对象
        InformationProto.Information information = (InformationProto.Information) msg;
        System.out.println("收到服务器端反馈的消息:" + information.getContent());

        //耗时操作
        service.submit(new Runnable() {
            @Override
            public void run() {
                //线程里操作要求捕捉异常
                try {
                    SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//设置日期格式
                    String strDate = df.format(new Date());
                    System.out.println("[->开始耗时任务]" + strDate);
                    Thread.sleep(5000);
                    strDate = df.format(new Date());
                    System.out.println("[->->执行完耗时任务]" + strDate);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

    }

    /**
     * 处理I/O事件的异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();//关闭当前Channel的连接
    }

    /**
     * 建立连接后该channelActive()方法只会被调用一次,这里的逻辑:建立连接后,字节序列被发送到服务器,编码格式是utf-8
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //线程中不能直接访问外部局部变量,两种解决方法
        //1.局部变量通过final修饰符,变为全局变量
        //2.在线程中创建属性、属性的赋值方法,然后在创建线程时,通过调用这个自身的方法,实现局部变量的方位.(推荐使用)


        //定时任务
        //第二个参数为0,代表客户端连接上服务器就立即执行,如果指定为5,代表5秒开始执行
        //第二个参数是间隔周期
        //第三个参数是时间单位
        ctx.channel().eventLoop().scheduleWithFixedDelay(new Runnable() {

            ChannelHandlerContext ctx;

            @Override
            public void run() {

                //发送自定义协议格式的消息
                byte[] header = new byte[]{(byte) 0x5A, (byte) 0x5A};
                byte[] msgtype = new byte[]{(byte) 0x01};
                SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//设置日期格式
                String strDate = df.format(new Date());

                InformationProto.Information information = InformationProto.Information.newBuilder()
                        .setHeader(ByteString.copyFrom(header))
                        .setMsgtype(ByteString.copyFrom(msgtype))
                        .setPersonnum(178)
                        .setPrice(10015.78f)
                        .setContent("客户端发的心跳包:" + strDate)
                        .build();

                ctx.writeAndFlush(information);

            }

            //对自身属性进行赋值
            public Runnable accept(ChannelHandlerContext chct) {
                this.ctx = chct;
                return this;
            }

        }.accept(ctx), 0, 3, TimeUnit.SECONDS);


    }
}






























































